/*
 * Naming convention:
 * 
 * jb* for JButton
 * jl* for JLabel
 * jm* for JMenu
 * jmi* for JMenuItem
 * jtf* for JTextField
 * jpm* for JMenuItem used in JPopupMenu
 * 
 */

package jnote;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.event.*;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Scanner;
import javax.swing.*;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import javax.swing.undo.UndoManager;

public class Jnote extends JFrame implements ActionListener {    
    private JButton jbAbout = new JButton(new ImageIcon("res/info.png"));
    private JButton jbUndo = new JButton(new ImageIcon("res/undo.png"));  
    private JButton jbOpen = new JButton(new ImageIcon("res/open.png"));
    private JButton jbColor = new JButton(new ImageIcon("res/color.png"));
    private JButton jbCopy = new JButton(new ImageIcon("res/copy.png"));
    private JButton jbFont = new JButton(new ImageIcon("res/font.png"));
    private JButton jbPaste = new JButton(new ImageIcon("res/paste.png"));
    private JButton jbNew = new JButton(new ImageIcon("res/new.png"));
    private JButton jbClear = new JButton(new ImageIcon("res/clear.png"));
    private JButton jbReplace = new JButton(new ImageIcon("res/replace.png"));
    private JButton jbReplaceAll = new JButton(new ImageIcon("res/replace_all.png"));
    private JButton jbRedo = new JButton(new ImageIcon("res/redo.png"));
    private JButton jbSave = new JButton(new ImageIcon("res/save.png"));
    private JButton jbSaveAs = new JButton(new ImageIcon("res/save_as.png"));
    private JButton jbCut = new JButton(new ImageIcon("res/cut.png"));
    
    private JMenu jmAbout = new JMenu();
    private JMenu jmFile = new JMenu();
    private JMenu jmEdit = new JMenu();
    private JMenu jmOptions = new JMenu();
    private JMenu jmRecentFiles = new JMenu();
    private JMenu jmFind = new JMenu();
    
    private JMenuItem jmiAbout = new JMenuItem(new ImageIcon("res/info.png"));
    private JMenuItem jmiUndo = new JMenuItem(new ImageIcon("res/undo.png"));
    private JMenuItem jmiOpen = new JMenuItem(new ImageIcon("res/open.png"));
    private JMenuItem jmiFind = new JMenuItem(new ImageIcon("res/search.png"));
    private JMenuItem jmiColor = new JMenuItem(new ImageIcon("res/color.png"));
    private JMenuItem jmiCopy = new JMenuItem(new ImageIcon("res/copy.png"));
    private JMenuItem jmiDate = new JMenuItem(new ImageIcon("res/timestamp.png"));
    private JMenuItem jmiExit = new JMenuItem(new ImageIcon("res/exit.png"));
    private JMenuItem jmiFont = new JMenuItem(new ImageIcon("res/font.png"));
    private JMenuItem jmiPaste = new JMenuItem(new ImageIcon("res/paste.png"));
    private JMenuItem jmiNew = new JMenuItem(new ImageIcon("res/new.png"));
    private JMenuItem jmiClear = new JMenuItem(new ImageIcon("res/clear.png"));
    private JMenuItem jmiClearRecentFiles = new JMenuItem(new ImageIcon("res/clear.png"));
    private JMenuItem jmiReplace = new JMenuItem(new ImageIcon("res/replace.png")); 
    private JMenuItem jmiRedo = new JMenuItem(new ImageIcon("res/redo.png"));
    private JMenuItem jmiSave = new JMenuItem(new ImageIcon("res/save.png"));
    private JMenuItem jmiSaveAs = new JMenuItem(new ImageIcon("res/save_as.png"));
    private JMenuItem jmiSelectAll = new JMenuItem(new ImageIcon("res/select_all.png"));
    private JMenuItem jmiCut = new JMenuItem(new ImageIcon("res/cut.png"));
    private JMenuItem jmiGoTo = new JMenuItem(new ImageIcon("res/go_to.png"));
    
    private JMenuItem jpmUndo = new JMenuItem(new ImageIcon("res/undo.png"));
    private JMenuItem jpmCopy = new JMenuItem(new ImageIcon("res/copy.png"));
    private JMenuItem jpmPaste = new JMenuItem(new ImageIcon("res/paste.png"));
    private JMenuItem jpmClear = new JMenuItem(new ImageIcon("res/clear.png"));
    private JMenuItem jpmRedo = new JMenuItem(new ImageIcon("res/redo.png"));
    private JMenuItem jpmSelectAll = new JMenuItem(new ImageIcon("res/select_all.png"));
    private JMenuItem jpmCut = new JMenuItem(new ImageIcon("res/cut.png"));
    
    private Clipboard clipboard = getToolkit().getSystemClipboard();
    private Image im = Toolkit.getDefaultToolkit().getImage("res/notepad.png");
    private JCheckBoxMenuItem jmiStatusbar = new JCheckBoxMenuItem();
    private JCheckBoxMenuItem jmiToolbar = new JCheckBoxMenuItem();
    private JLabel jlFind = new JLabel("Find ");
    private JLabel jlReplace = new JLabel(" Replace with ");
    private JMenuBar menuBar = new JMenuBar();
    private JPopupMenu popup = new JPopupMenu();
    private JTextField jtfFind = new JTextField("", 20);
    private JTextField jtfReplace = new JTextField("", 20);
    private JToolBar toolbar = new JToolBar();
    private JTextArea textArea = new JTextArea();
    private JScrollPane scrollArea = new JScrollPane(textArea);
    private StatusBar statusbar = new StatusBar();
    private String currentFile = "";
    private UndoManager um = new UndoManager();    
    private boolean saved = false;
    private int position = 0;
    private int length = 0;

    Jnote() {
        /*
         * Set title, icon, size and center the window.
         */
        setTitle("JNote");
        setIconImage(im);
        setSize(800, 600);
        setDefaultCloseOperation(EXIT_ON_CLOSE);    
        setLocationRelativeTo(null);

        /*
         * Init menu, toolbar and right click menu.
         */
        initMenu();
        initToolbar();
        initPopupMenu();

        /*
         * Set Font, type and dimension.
         */
        textArea.setFont(new Font("Times New Roman", Font.PLAIN, 16));
        
        /*
         * Set a layout.
         */
        getContentPane().setLayout(new BorderLayout());
        
        /*
         * Add the toolbar, the scroll pane and the statusbar.
         */
        getContentPane().add(toolbar, BorderLayout.PAGE_START);
        getContentPane().add(scrollArea, BorderLayout.CENTER);
        getContentPane().add(statusbar, BorderLayout.PAGE_END);
        
        /* 
         * Set the number of the edits that UndoManager can handle.
         * A negative number means that can handle infinite edits.
         */
        um.setLimit(-1);

        /*
         * Add the UndoManager to the JTextArea.
         */
        textArea.getDocument().addUndoableEditListener(um);
        textArea.setLineWrap(true);

        /*
         * Add the automatic scroll .
         */
        scrollArea.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
            @Override
            public void adjustmentValueChanged(AdjustmentEvent e) {
                textArea.select(textArea.getCaretPosition(), 0);
            }
        });
        
        jtfFind.addCaretListener(new CaretListener() {
            @Override
            public void caretUpdate(CaretEvent e) {
                /*
                 * Look for the text everytime caret is moving.
                 */
                findText();
            }
        });
        
        textArea.addKeyListener(new KeyListener() {
            @Override
            public void keyTyped(KeyEvent e) {
                saved = false;
            }

            @Override
            public void keyPressed(KeyEvent e) {
                saved = false;
            }

            @Override
            public void keyReleased(KeyEvent e) {
                saved = false;
            }
        });
        
        textArea.addCaretListener(new CaretListener() {
            @Override
            public void caretUpdate(CaretEvent e) {
                saved = e.getDot() <= 0;                
                getCaretXYPosition();
            }
            
            private void getCaretXYPosition() {
                try {
                    /*
                     * Return the caret's position.
                     */
                    int caretPosition = textArea.getCaretPosition();
                    
                    /*
                     * Retrieve the X and Y position of the caret.
                     */
                    int row = textArea.getLineOfOffset(caretPosition);
                    int column = caretPosition - textArea.getLineStartOffset(row);
                
                    /* 
                     * Update the values in the statusbar.
                     */
                    statusbar.setText("Row " + (row + 1) + 
                                      ", Column " + (column + 1) + 
                                      " : Characters " + getNumChars(textArea));
                } catch(BadLocationException ble) {
                }
            }
            
            private int getNumChars(JTextComponent tx) {
                /*
                 * Get the total number of characters.
                 */
                return tx.getText().length();
            }
        });
        
        this.addWindowListener(new WindowListener() {
            @Override
            public void windowOpened(WindowEvent e) {
            }

            @Override
            public void windowClosing(WindowEvent e) {
                /*
                 * If file is not saved but modified
                 * ask to save it before closing the program.
                 */
                if(!saved) {
                    if(JOptionPane.showConfirmDialog(null, 
                                                     "Do you want to save the current file ?", 
                                                     "Save file", 
                                                     JOptionPane.YES_NO_OPTION,  
                                                     JOptionPane.WARNING_MESSAGE) == 0) {
                        saveFileAs();
                        System.exit(0);
                    } else {
                        System.exit(0);
                    }
                } else {
                    System.exit(0);
                }
            }

            @Override
            public void windowClosed(WindowEvent e) {
            }            

            @Override
            public void windowDeiconified(WindowEvent e) {
            }

            @Override
            public void windowActivated(WindowEvent e) {
                saved = true;
            }

            @Override
            public void windowDeactivated(WindowEvent e) {
            }

            @Override
            public void windowIconified(WindowEvent e) {
            }
        });
        
        textArea.addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent e) {
            }

            @Override
            public void mousePressed(MouseEvent e) {
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                showPopupMenu(e);
            }

            @Override
            public void mouseEntered(MouseEvent e) {
            }

            @Override
            public void mouseExited(MouseEvent e) {
            }
            
            private void showPopupMenu(MouseEvent e) {
                if(e.isPopupTrigger()) {
                    if(!clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
                        jpmPaste.setEnabled(false);
                    } else {
                        jpmPaste.setEnabled(true);
                    }
                    
                    if(!um.canUndo()) {
                        jpmUndo.setEnabled(false);
                    } else {
                        jpmUndo.setEnabled(true);
                    }
                    
                    if(!um.canRedo()) {
                        jpmRedo.setEnabled(false);
                    } else {
                        jpmRedo.setEnabled(true);
                    } 
                    
                    if(textArea.getText().equals("")) {
                        jpmCopy.setEnabled(false);
                        jpmCut.setEnabled(false);
                        jpmSelectAll.setEnabled(false);
                        jpmClear.setEnabled(false);
                    } else {
                        jpmCopy.setEnabled(true);
                        jpmCut.setEnabled(true);
                        jpmSelectAll.setEnabled(true);
                        jpmClear.setEnabled(true);
                    }
                    popup.show(e.getComponent(), e.getX(), e.getY());
                }
            }
        });
    }
    
    /*
     * Init menu's components
     */
    private void initMenu() {
        /*
         * Add menubar to the program
         */
        setJMenuBar(menuBar);
        menuBar.add(jmFile);
        menuBar.add(jmEdit);
        menuBar.add(jmFind);
        menuBar.add(jmOptions);
        menuBar.add(jmAbout);      

        jmFile.setText("File");
        jmEdit.setText("Edit");
        jmFind.setText("Find");
        jmOptions.setText("Options");
        jmAbout.setText("About");
        
        /*
         * Set option's name, keyboard shortcut, action listener
         * and add to the menu
         */
        jmiNew.setText("New file");
        jmiNew.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_MASK));
        jmiNew.addActionListener(this);
        jmFile.add(jmiNew);
        
        
        jmiOpen.setText("Open file");               
        jmiOpen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK));        
        jmiOpen.addActionListener(this); 
        jmFile.add(jmiOpen);
        
        jmiSave.setText("Save file");
        jmiSave.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK));
        jmiSave.addActionListener(this);
        jmFile.add(jmiSave);
        
        jmiSaveAs.setText("Save file as...");
        jmiSaveAs.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
        jmiSaveAs.addActionListener(this);
        jmFile.add(jmiSaveAs);        
        jmFile.addSeparator();
        
        jmRecentFiles.setText("Recent files");
        jmRecentFiles.addActionListener(this);
        jmRecentFiles.setIcon(new ImageIcon("res/recent_files.png"));
        jmFile.add(jmRecentFiles);
        
        jmiClearRecentFiles.setText("Clear recent files");
        jmiClearRecentFiles.addActionListener(this);
        jmFile.add(jmiClearRecentFiles);
        jmFile.addSeparator();

        jmiExit.setText("Quit");
        jmiExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.ALT_MASK));
        jmiExit.addActionListener(this);
        jmFile.add(jmiExit);
        
        jmiCopy.setText("Copy");
        jmiCopy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK));
        jmiCopy.addActionListener(this);
        jmEdit.add(jmiCopy);
        
        jmiPaste.setText("Paste");
        jmiPaste.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK));
        jmiPaste.addActionListener(this);
        jmEdit.add(jmiPaste);
        
        jmiCut.setText("Cut");
        jmiCut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK));
        jmiCut.addActionListener(this);
        jmEdit.add(jmiCut);
        
        jmiSelectAll.setText("Select All");
        jmiSelectAll.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_MASK));
        jmiSelectAll.addActionListener(this);
        jmEdit.add(jmiSelectAll);
        
        jmiClear.setText("Clear");
        jmiClear.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.CTRL_MASK));
        jmiClear.addActionListener(this);
        jmEdit.add(jmiClear);
        
        jmiUndo.setText("Undo");
        jmiUndo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_MASK));
        jmiUndo.addActionListener(this);
        jmEdit.add(jmiUndo);
        
        jmiRedo.setText("Redo");
        jmiRedo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_MASK));
        jmiRedo.addActionListener(this);
        jmEdit.add(jmiRedo);
        
        jmiFind.setText("Find");  
        jmiFind.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_MASK));
        jmiFind.addActionListener(this);
        jmFind.add(jmiFind);
        
        jmiReplace.setText("Replace"); 
        jmiReplace.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK));
        jmiReplace.addActionListener(this); 
        jmFind.add(jmiReplace);
        
        jmiGoTo.setText("Go to..."); 
        jmiGoTo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_G, InputEvent.CTRL_MASK));
        jmiGoTo.addActionListener(this); 
        jmFind.add(jmiGoTo);
        
        jmiFont.setText("Font");
        jmiFont.addActionListener(this); 
        jmOptions.add(jmiFont);
        
        jmiColor.setText("Color");
        jmiColor.addActionListener(this);
        jmOptions.add(jmiColor);
        
        jmiDate.setText("Date");
        jmiDate.addActionListener(this);
        jmOptions.add(jmiDate);        
        jmOptions.addSeparator();
        
        jmiStatusbar.setText("Show Statusbar");
        jmiStatusbar.addActionListener(this);
        jmiStatusbar.setSelected(true);
        jmOptions.add(jmiStatusbar);
        
        jmiToolbar.setText("Show Toolbar");
        jmiToolbar.addActionListener(this);
        jmiToolbar.setSelected(true);
        jmOptions.add(jmiToolbar);
        
        jmiAbout.setText("About"); 
        jmiAbout.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.CTRL_MASK));
        jmiAbout.addActionListener(this); 
        jmAbout.add(jmiAbout);
    }
    
    /*
     * Init toolbar and add it to the program
     */
    private void initToolbar() {
        toolbar.setRollover(true);
        toolbar.setFloatable(false);
        
        jbNew.setToolTipText("New file");
        jbNew.addActionListener(this);
        jbNew.setFocusable(false);
        
        jbOpen.setToolTipText("Open file");
        jbOpen.addActionListener(this);
        jbOpen.setFocusable(false);
        
        jbSave.setToolTipText("Save file");
        jbSave.addActionListener(this);
        jbSave.setFocusable(false);
        
        jbSaveAs.setToolTipText("Save file as...");
        jbSaveAs.addActionListener(this);
        jbSaveAs.setFocusable(false);
        
        jbUndo.setToolTipText("Undo");
        jbUndo.addActionListener(this);
        jbUndo.setFocusable(false);
        
        jbRedo.setToolTipText("Redo");
        jbRedo.addActionListener(this);
        jbRedo.setFocusable(false);
        
        jbCut.setToolTipText("Cut");
        jbCut.addActionListener(this);
        jbCut.setFocusable(false);
        
        jbCopy.setToolTipText("Copy");
        jbCopy.addActionListener(this);
        jbCopy.setFocusable(false);
        
        jbPaste.setToolTipText("Paste");
        jbPaste.addActionListener(this);
        jbPaste.setFocusable(false);
        
        jbClear.setToolTipText("Clear");
        jbClear.addActionListener(this);
        jbClear.setFocusable(false);
        
        jbFont.setToolTipText("Change Font");
        jbFont.addActionListener(this);
        jbFont.setFocusable(false);
        
        jbColor.setToolTipText("Change Color");
        jbColor.addActionListener(this);
        jbColor.setFocusable(false);
        
        jbAbout.setToolTipText("About JNote");
        jbAbout.addActionListener(this);
        jbAbout.setFocusable(false);
        
        jbReplace.setToolTipText("Replace text");
        jbReplace.addActionListener(this);
        jbReplace.setFocusable(false);
        
        jbReplaceAll.setToolTipText("Replace all occurrences");
        jbReplaceAll.addActionListener(this);
        jbReplaceAll.setFocusable(false);
        
        jtfFind.setFocusable(false);
        jtfReplace.setFocusable(false);
        
        toolbar.add(jbNew);
        toolbar.add(jbOpen);
        toolbar.add(jbSave);
        toolbar.add(jbSaveAs);
        toolbar.addSeparator();
        toolbar.add(jbCut);
        toolbar.add(jbCopy);
        toolbar.add(jbPaste);
        toolbar.add(jbClear);
        toolbar.addSeparator();
        toolbar.add(jbUndo);
        toolbar.add(jbRedo);
        toolbar.addSeparator();
        toolbar.add(jbFont);
        toolbar.add(jbColor);
        toolbar.addSeparator();
        toolbar.add(jlFind);
        toolbar.add(jtfFind);
        toolbar.add(jlReplace);
        toolbar.add(jtfReplace);
        toolbar.add(jbReplace);
        toolbar.add(jbReplaceAll);
        toolbar.addSeparator();
        toolbar.add(jbAbout);
        
        jtfFind.addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent e) {
                jtfFind.setFocusable(true);
                jtfFind.requestFocusInWindow();
            }

            @Override
            public void mousePressed(MouseEvent e) {
                jtfFind.setFocusable(true);
                jtfFind.requestFocusInWindow();
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                jtfFind.setFocusable(true);
                jtfFind.requestFocusInWindow();
            }

            @Override
            public void mouseEntered(MouseEvent e) {
            }

            @Override
            public void mouseExited(MouseEvent e) {
            }
        });
        
        jtfReplace.addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent e) {
                jtfReplace.setFocusable(true);
                jtfReplace.requestFocusInWindow();
            }

            @Override
            public void mousePressed(MouseEvent e) {
                jtfReplace.setFocusable(true);
                jtfReplace.requestFocusInWindow();
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                jtfReplace.setFocusable(true);
                jtfReplace.requestFocusInWindow();
            }

            @Override
            public void mouseEntered(MouseEvent e) {
            }

            @Override
            public void mouseExited(MouseEvent e) {
            }
        });
    }
    
    /*
     * Init right click menu
     */
    private void initPopupMenu() {
        popup.add(jpmCopy);
        popup.add(jpmCut);
        popup.add(jpmPaste);
        popup.add(jpmUndo);
        popup.add(jpmRedo);
        popup.add(jpmSelectAll);
        popup.add(jpmClear);
        
        jpmCopy.setText("Copy");
        jpmCopy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK));
        jpmCopy.addActionListener(this);
        
        jpmPaste.setText("Paste");
        jpmPaste.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK));
        jpmPaste.addActionListener(this);
        
        jpmCut.setText("Cut");
        jpmCut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK));
        jpmCut.addActionListener(this);
        
        jpmSelectAll.setText("Select All");
        jpmSelectAll.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_MASK));
        jpmSelectAll.addActionListener(this);
        
        jpmClear.setText("Clear");
        jpmClear.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.CTRL_MASK));
        jpmClear.addActionListener(this);
        
        jpmUndo.setText("Undo");
        jpmUndo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_MASK));
        jpmUndo.addActionListener(this);
        
        jpmRedo.setText("Redo");
        jpmRedo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_MASK));
        jpmRedo.addActionListener(this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        /*
         * Check the source of the Event
         */
        if(e.getSource() == jmiExit) {
            /*
             * If the file is modified and not saved,
             * prompt to save it before leaving the program
             */
            if(!saved) {
                if(JOptionPane.showConfirmDialog(this, 
                                                 "Do you want to save the file ?", 
                                                 "Save file", 
                                                 JOptionPane.YES_NO_OPTION, 
                                                 JOptionPane.WARNING_MESSAGE) == 0) {
                    saveFileAs();
                    System.exit(0);
                } else {
                    System.exit(0);
                }
            } else {
                System.exit(0);
            }
        } else if(e.getSource() == jmiNew || e.getSource() == jbNew) {
            /*
             * Before to create a new file, check if the current one has been modified.
             * If so, prompt to save it.
             */
            if(!(textArea.getText().equals("")) && !saved) {
                if(JOptionPane.showConfirmDialog(this, 
                                                 "File has been modified.\nDo you want to save it ?", 
                                                 "Save file", 
                                                 JOptionPane.YES_NO_OPTION, 
                                                 JOptionPane.WARNING_MESSAGE) == 0) {
                    saveFileAs();
                    saved = true;
                    newFile();
                } else {
                    newFile();
                    saved = false;
                }
            } else {
                newFile();
                saved = false;
            }
        } else if(e.getSource() == jmiOpen || e.getSource() == jbOpen) {
            openFile();
        } else if(e.getSource() == jmiSaveAs || e.getSource() == jbSaveAs) {
            saveFileAs();
        } else if(e.getSource() == jmiSave || e.getSource() == jbSave) {
            /*
             * If the current file is already saved, use the same filename
             * otherwise ask to save it with a name.
             */
            if(saved) {
                saveFile();
            } else {
                saveFileAs();
            }
        } else if(e.getSource() == jmiClearRecentFiles) {
            jmRecentFiles.removeAll();
        } else if(e.getSource() == jmiCut || e.getSource() == jbCut || e.getSource() == jpmCut) {
            Cut c = new Cut();
            c.cut(clipboard, textArea);
        } else if(e.getSource() == jmiCopy || e.getSource() == jbCopy || e.getSource() == jpmCopy) {
            Copy c = new Copy();
            c.copy(clipboard, textArea);
        } else if(e.getSource() == jmiPaste || e.getSource() == jbPaste || e.getSource() == jpmPaste) {
            Paste p = new Paste();
            p.paste(clipboard, textArea);
        } else if(e.getSource() == jmiSelectAll || e.getSource() == jpmSelectAll) {
            textArea.selectAll();
        } else if(e.getSource() == jmiClear || e.getSource() == jbClear || e.getSource() == jpmClear) {
            textArea.setText("");
            textArea.setCaretPosition(0);
        } else if(e.getSource() == jmiUndo || e.getSource() == jbUndo || e.getSource() == jpmUndo) {
            /*
             * Check if undo is available.
             */
            if(um.canUndo()) {
                um.undo();
            }
        } else if(e.getSource() == jmiRedo || e.getSource() == jbRedo || e.getSource() == jpmRedo) {
            /*
             * Check if redo is available.
             */
            if(um.canRedo()) {
                um.redo();
            }
        } else if(e.getSource() == jmiFind) {
            FindReplace fr = new FindReplace(textArea);
            fr.tabFindReplace.setSelectedIndex(0);
            fr.setVisible(true);
        } else if(e.getSource() == jmiReplace) {
            FindReplace fr = new FindReplace(textArea);
            fr.tabFindReplace.setSelectedIndex(1);
            fr.setVisible(true);
        } else if(e.getSource() == jmiGoTo) {
            try {
                String goTo = JOptionPane.showInputDialog(this, "Go to...");
                
                if(!findText(goTo)) {
                    JOptionPane.showMessageDialog(this, "Text not found");
                }         
            } 
            catch(NullPointerException npe) {
            }
        } else if(e.getSource() == jmiFont || e.getSource() == jbFont) {
            FontChooser fc = new FontChooser(textArea.getFont());
            fc.setVisible(true);
            textArea.setFont(fc.getSelectedFont());
        } else if(e.getSource() == jmiColor || e.getSource() == jbColor) {
            ColorChooser c = new ColorChooser();
            textArea.setForeground(c.returnColor());
        } else if(e.getSource() == jmiDate) {
            /*
             * TODO: English days of the week.
             */
            String days[] = new DateFormatSymbols().getWeekdays();
            Calendar date = Calendar.getInstance();
            GregorianCalendar gc = new GregorianCalendar();
            SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
            textArea.append(days[date.get(Calendar.DAY_OF_WEEK)] + " " + sdf.format(gc.getTime()));
        } else if(e.getSource() == jmiStatusbar) {
            if(!jmiStatusbar.isSelected()) {
                statusbar.setVisible(false);
            } else {
                statusbar.setVisible(true);
            }
        } else if(e.getSource() == jmiToolbar) {
            if(!jmiToolbar.isSelected()) {
                toolbar.setVisible(false);
            } else {
                toolbar.setVisible(true);
            }
        } else if(e.getSource() == jbReplace) {
            replaceText();
        } else if(e.getSource() == jbReplaceAll) {
            replaceAll();
        } else if(e.getSource() == jmiAbout || e.getSource() == jbAbout) {
            About about = new About();
            about.setVisible(true);
        }
    }
    
    /*
     * Return true if the text to search is found,
     * false otherwise.
     */
    public boolean findText() {
        String toFind = jtfFind.getText();
        String text = textArea.getText();
        Highlighter h = textArea.getHighlighter();
        h.removeAllHighlights();
        int pos = text.indexOf(toFind, 0);
        
        if(text.contains(toFind) && !(toFind.equals(""))) {
            jtfFind.setBackground(Color.GREEN);
            try {
                h.addHighlight(pos, pos + toFind.length(), DefaultHighlighter.DefaultPainter);
                position = pos;
                length = pos + toFind.length();
                return true;
            } catch(BadLocationException ble) {
            }
        }
        
        else if(toFind.equals("") && text.equals("") || 
                (toFind.equals("") && !(text.equals("")))) {
            jtfFind.setBackground(Color.WHITE);
        } else {
            jtfFind.setBackground(Color.RED);
        }
        return false;
    }
    
    public boolean findText(String text) {
        if(textArea.getText().contains(text)) {
            /*
             * Return the index of the given word
             * and move the caret to the end of it.
             */
            int pos = textArea.getText().indexOf(text, 0);
            textArea.setCaretPosition(pos + text.length());
            return true;
        }
	return false;
    }
    
    public void replaceText() {
        if(findText() && !(jtfReplace.getText().isEmpty())) {
            textArea.select(position, length);
            textArea.replaceSelection(jtfReplace.getText());
            textArea.getHighlighter().removeAllHighlights();
            jtfFind.setBackground(Color.WHITE);
        } else if(!findText() && !(jtfReplace.getText().isEmpty())) {
            JOptionPane.showMessageDialog(this, "Text not found");        
        } else if(findText() && jtfReplace.getText().isEmpty()) {
            JOptionPane.showMessageDialog(this, "Nothing to replace");
        } else if(jtfFind.getText().equals(jtfReplace.getText())) {
            JOptionPane.showMessageDialog(this, "Text to find and text to replace are equals");
        } else if(textArea.getText().isEmpty()) {
            JOptionPane.showMessageDialog(this, "File is empty");
        }
    }
    
    public void replaceAll() {
        int occurrences = 0;        
        do {
            if(!findText() && !(jtfReplace.getText().isEmpty())) {
                JOptionPane.showMessageDialog(this, "Text not found");
                return;
            } else if(findText() && jtfReplace.getText().isEmpty()) {
                JOptionPane.showMessageDialog(this, "Nothing to replace");
                return;
            } else if(jtfFind.getText().equals(jtfReplace.getText())) {
                JOptionPane.showMessageDialog(this, "Text to find and text to replace are equals");
                return;
            } else if(textArea.getText().isEmpty()) {
                JOptionPane.showMessageDialog(this, "File is empty");
                return;
            } else {
                textArea.select(position, length);
                textArea.replaceSelection(jtfReplace.getText());
                textArea.getHighlighter().removeAllHighlights();
                jtfFind.setBackground(Color.WHITE);
            }
            occurrences++;
        }
        while(findText() && !(jtfReplace.getText().isEmpty()));
        
        JOptionPane.showMessageDialog(this, occurrences + " occurrences replaced");
    }
    
    private void openFile() {
        /*
         * Open a FileChooser and set the FileFilter for known types.
         */
        JFileChooser open = new JFileChooser();
        open.setFileFilter(new FileFilterIni());
        open.setFileFilter(new FileFilterNfo());
        open.setFileFilter(new FileFilterTxt());

        int option = open.showOpenDialog(this);
        if(option == JFileChooser.APPROVE_OPTION) {
            /*
             * Before loading a new file, clear the text area.
             */
            textArea.setText("");
            try(Scanner scan = new Scanner(new FileReader(open.getSelectedFile().getPath()))) {
                setTitle("JNote - " + open.getSelectedFile().getPath());
                currentFile = open.getSelectedFile().getPath();

                /*
                 * Read a line until we reach the EOF
                 */
                while(scan.hasNext()) {
                    textArea.append(scan.nextLine() + "\n");
                }
            } catch(Exception ex) {
            }            
            saved = true;
            jmRecentFiles.add(currentFile);
        }
    }
    
    private void saveFileAs() {
        JFileChooser save = new JFileChooser();
        save.setFileFilter(new FileFilterIni());
        save.setFileFilter(new FileFilterNfo());
        save.setFileFilter(new FileFilterTxt());
        
        int option = save.showSaveDialog(this);
        if(option == JFileChooser.APPROVE_OPTION) {
            /*
             * If the file does not exists, save it.
             */
            if(!(save.getSelectedFile().exists())) {
                /*
                 * Automatically append the correct extension.
                 */
                String ext = addExtension(save);
                try(BufferedWriter out = new BufferedWriter(
                        new FileWriter(save.getSelectedFile().getPath().concat(ext)))) {
                    out.write(textArea.getText());
                    currentFile = save.getSelectedFile().getPath().concat(ext);
                } catch(Exception ex) {
                }
            } else {
                String ext = addExtension(save);
                
                /*
                 * If the file does exists, 
                 * prompt to overwrite it or not.
                 */
                if(JOptionPane.showConfirmDialog(this,
                                                 "File " + save.getName(save.getSelectedFile()) + " does exists, overwrite it ?",
                                                 "Save file as...", 
                                                 JOptionPane.YES_NO_OPTION, 
                                                 JOptionPane.WARNING_MESSAGE) == 0) {
                    try(BufferedWriter out = new BufferedWriter(
                            new FileWriter(save.getSelectedFile().getPath()))) {
                        out.write(textArea.getText());
                        currentFile = save.getSelectedFile().getPath().concat(ext);
                    } catch(Exception ex) {
                    }
                }
            }            
            saved = true;
            setTitle("JNote - " + currentFile);
            jmRecentFiles.add(currentFile);
        }
    }
    
    private void saveFile() {
        try(BufferedWriter out = new BufferedWriter(new FileWriter(currentFile))) {
            out.write(textArea.getText());
        } catch(Exception ex) {
        }        
        saved = true;
    }
    
    private void newFile() {
        setTitle("JNote");
        currentFile = "";
        textArea.setText("");
        textArea.setCaretPosition(0);
    }
    
    /*
     * Check the description of the corresponding FileFilter
     * and get the extension from it.
     */
    private String addExtension(JFileChooser fc) {
        String desc = fc.getFileFilter().getDescription();
        String ext = desc.substring(desc.indexOf(".", 0), desc.length() - 1);
        
        switch(ext) {
            case ".txt":
                return ".txt";
                
            case ".ini":    
                return ".ini";
            
            case ".nfo":    
                return ".nfo";
                
            default:
                return "";
        }
    }
    
    public static void main(String args[]) {
        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
        } catch(UnsupportedLookAndFeelException | ClassNotFoundException | 
                InstantiationException | IllegalAccessException e) {
        }        
        Jnote app = new Jnote();
        app.setVisible(true);
    }
}
